import { defineGlobalProperty, isRoomInterval } from '@/ayaka'
import SpawnController, { getCreeps } from '@/modules/structures/spawn'
import { MineMemory } from './types'
import { CreepRoles, SpawnTask } from '@/modules/structures/spawn/types'
import { BodyPartArrayType } from '@/utils/common'
import StructureCache from '@/modules/structureCache'
import { minerWork } from './miner'
import { RoomSafeState } from '@/room/state/types'

/**
 * 挖元素矿任务管理器
 */
export default class MineTaskManager {
    /**
     * 不可实例化
     */
    protected constructor() {
        throw new Error('MineTaskManager 不可被实例化！')
    }

    /**
     * 获取 memory
     */
    public static getMemory(room: Room) {
        if (!room.memory.mine) {
            const mineral = room.find(FIND_MINERALS)[0]
            room.memory.mine = {
                mineralId: mineral.id,
                miner: [],
                cooldown: mineral.ticksToRegeneration || 0,
            }
        }
        return room.memory.mine
    }

    /**
     * 运行
     */
    public static run(room: MyRoom) {
        if (room.controller.level < 6) return

        const memory = MineTaskManager.getMemory(room)
        if (memory.cooldown) {
            memory.cooldown--
            return
        }

        if (!memory.containerId || !memory.extractorId || room.memory.state.safe?.state !== RoomSafeState.normal) {
            return
        }

        const miners = MineTaskManager.getMiner(room, memory.miner)
        MineTaskManager.spawnMiner(room, miners, memory)
        minerWork(room, miners, memory)
    }

    /**
     * 判断能否并孵化新的 miner
     */
    protected static spawnMiner(room: MyRoom, creeps: Creep[], memory: MineMemory) {
        if (!isRoomInterval(room, 63)) return

        if (SpawnController.searchSpawnTaskByRole(room, CreepRoles.miner).length) return

        if (creeps.length) return

        // 仓库有位置放元素矿才孵化，先找一个有空间的仓库
        let target: StructureStorage | StructureTerminal | null = null
        if (room.storage && room.storage.store.getFreeCapacity() > 50000) target = room.storage
        else if (room.terminal && room.terminal.store.getFreeCapacity() > 30000) target = room.terminal
        if (!target) return

        MineTaskManager.addMinerSpawnTask(room, memory.miner, [
            [WORK, 40],
            [MOVE, 10],
        ])
    }

    /**
     * 获取矿工
     */
    protected static getMiner(room: MyRoom, creepNames: string[]) {
        return getCreeps(room, creepNames)
    }

    /**
     * 添加 miner 的孵化任务
     */
    protected static addMinerSpawnTask(room: MyRoom, creepNames: string[], body: BodyPartArrayType) {
        const task: SpawnTask = {
            role: CreepRoles.miner,
            body,
            defaultAdaption: true,
        }

        const creepName = SpawnController.addSpawnTask(room, task)
        if (!creepName) return

        creepNames.push(creepName)
    }

    /**
     * 注册 mineralContainer
     */
    public static registerContainer(room: MyRoom, containers: StructureContainer[]) {
        const memory = MineTaskManager.getMemory(room)
        delete memory.containerId
        const mineral = Game.getObjectById(memory.mineralId)!

        return containers.filter((container) => {
            if (!memory.containerId && container.pos.isNearTo(mineral)) {
                memory.containerId = container.id
                return false
            }
            return true
        })
    }

    /**
     * 注册 extractor
     */
    public static registerExtractor(room: MyRoom, extractors: StructureExtractor[]) {
        const memory = MineTaskManager.getMemory(room)
        delete memory.extractorId

        return extractors.filter((extractor) => {
            if (!memory.extractorId) {
                memory.extractorId = extractor.id
                return false
            }
            return true
        })
    }

    /**
     * 注销建筑
     */
    public static unregisterMine(room: MyRoom, types: BuildableStructureConstant[]) {
        if (!room.memory.mine) return

        if (room.memory.mine.containerId && types.includes(STRUCTURE_CONTAINER)) {
            const container = Game.getObjectById(room.memory.mine.containerId)
            if (!container) {
                delete room.memory.mine.containerId
            }
        }

        if (room.memory.mine.extractorId && types.includes(STRUCTURE_EXTRACTOR)) {
            const extractor = Game.getObjectById(room.memory.mine.extractorId)
            if (!extractor) {
                delete room.memory.mine.extractorId
            }
        }
    }
}

StructureCache.onStructMissing(function unregisterMine(room, types) {
    MineTaskManager.unregisterMine(room, types)
})

StructureCache.onNewStruct(function registerExtractor(room, newStructMap) {
    const extractors = newStructMap[STRUCTURE_EXTRACTOR]
    if (extractors) MineTaskManager.registerExtractor(room, extractors)
})

defineGlobalProperty('Mine', {
    value: MineTaskManager,
})
